// Network.cpp: implementation of the CNetwork class.
// !F RCON system - lluo
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "Network.h"
#include "Utils.h"

//#include "platform.h"
#if defined(LINUX)
#include "INetwork.h"
#endif

#include "IConsole.h"

#include "DebugKit/NetworkInspector.h"

#include "Protocol/NetNub.h"
#include "Protocol/NetChannel.h"
#include "Protocol/Serialize.h"

#include "Context/ContextView.h"
#include "Context/NetContext.h"

#include "Compression/CompressionManager.h"

#include "Services/ServiceManager.h"
#include "Services/CryLAN/LanQueryListener.h"

#include "VOIP/VoiceManager.h"
#include "VOIP/IVoiceEncoder.h"
#include "VOIP/IVoiceDecoder.h"

#include "DistributedLogger.h"
#include "DebugKit/DebugKit.h"

#include "RemoteControl/RemoteControl.h"
#include "Http/SimpleHttpServer.h"

#include "DebugKit/ServerProfiler.h"

#include "ThreadProfilerSupport.h"
#include "ITextModeConsole.h"
#include "Http/AutoConfigDownloader.h"

#include "Lobby/CryLobby.h"
#include "Lobby/CryMatchMaking.h"
#include "NetProfile.h"

#include "Cryptography/StreamCipher.h"

#include "Protocol/PacketRateCalculator.h"
#if USE_GFWL
#include <Winxnet.h>
#endif

#if ENABLE_OBJECT_COUNTING
#include "PsApi.h"
#endif





static const int OCCASIONAL_TICKS = 50;

SSystemBranchCounters g_systemBranchCounters;
SObjectCounters g_objcnt;
static SSystemBranchCounters g_lastSystemBranchCounters;

static CAutoConfigDownloader * g_pFileDownloader;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CNetwork * CNetwork::m_pThis = 0;

int g_QuickLogY;
CTimeValue g_QuickLogTime;
CTimeValue g_time;
int g_lockCount = 0;

struct CNetwork::SNetError CNetwork::m_neNetErrors[]=
{
	{NET_OK, "No Error"},
	{NET_FAIL, "Generic Error"},
	// SOCKET
	{NET_EINTR, "WSAEINTR - interrupted function call"},
	{NET_EBADF, "WSAEBADF - Bad file number"},
	{NET_EACCES, "WSAEACCES - error in accessing socket"},
	{NET_EFAULT, "WSAEFAULT - bad address"},
	{NET_EINVAL, "WSAEINVAL - invalid argument"},
	{NET_EMFILE, "WSAEMFILE - too many open files"},
	{NET_EWOULDBLOCK, "WSAEWOULDBLOCK - resource temporarily unavailable"},
	{NET_EINPROGRESS, "WSAEINPROGRESS - operation now in progress"},
	{NET_EALREADY, "WSAEALREADY - operation already in progress"},
	{NET_ENOTSOCK, "WSAENOTSOCK - socket operation on non-socket"},
	{NET_EDESTADDRREQ, "WSAEDESTADDRREQ - destination address required"},
	{NET_EMSGSIZE, "WSAEMSGSIZE - message to long"},
	{NET_EPROTOTYPE, "WSAEPROTOTYPE - protocol wrong type for socket"},
	{NET_ENOPROTOOPT, "WSAENOPROTOOPT - bad protocol option"},
	{NET_EPROTONOSUPPORT, "WSAEPROTONOSUPPORT - protocol not supported"},
	{NET_ESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT - socket type not supported"},
	{NET_EOPNOTSUPP, "WSAEOPNOTSUPP - operation not supported"},
	{NET_EPFNOSUPPORT, "WSAEPFNOSUPPORT - protocol family not supported"},
	{NET_EAFNOSUPPORT, "WSAEAFNOSUPPORT - address family not supported by protocol"},
	{NET_EADDRINUSE, "WSAEADDRINUSE - address is in use"},
	{NET_EADDRNOTAVAIL, "WSAEADDRNOTAVAIL - address is not valid in context"},
	{NET_ENETDOWN, "WSAENETDOWN - network is down"},
	{NET_ENETUNREACH, "WSAENETUNREACH - network is unreachable"},
	{NET_ENETRESET, "WSAENETRESET - network dropped connection on reset"},
	{NET_ECONNABORTED, "WSACONNABORTED - software caused connection aborted"},
	{NET_ECONNRESET, "WSAECONNRESET - connection reset by peer"},
	{NET_ENOBUFS, "WSAENOBUFS - no buffer space available"},
	{NET_EISCONN, "WSAEISCONN - socket is already connected"},
	{NET_ENOTCONN, "WSAENOTCONN - socket is not connected"},
	{NET_ESHUTDOWN, "WSAESHUTDOWN - cannot send after socket shutdown"},
	{NET_ETOOMANYREFS, "WSAETOOMANYREFS - Too many references: cannot splice"},
	{NET_ETIMEDOUT, "WSAETIMEDOUT - connection timed out"},
	{NET_ECONNREFUSED, "WSAECONNREFUSED - connection refused"},
	{NET_ELOOP, "WSAELOOP - Too many levels of symbolic links"},
	{NET_ENAMETOOLONG, "WSAENAMETOOLONG - File name too long"},
	{NET_EHOSTDOWN, "WSAEHOSTDOWN - host is down"},
	{NET_EHOSTUNREACH, "WSAEHOSTUNREACH - no route to host"},
	{NET_ENOTEMPTY, "WSAENOTEMPTY - Cannot remove a directory that is not empty"},
	{NET_EUSERS, "WSAEUSERS - Ran out of quota"},
	{NET_EDQUOT, "WSAEDQUOT - Ran out of disk quota"},
	{NET_ESTALE, "WSAESTALE - File handle reference is no longer available"},
	{NET_EREMOTE, "WSAEREMOTE - Item is not available locally"},

	// extended winsock errors(not BSD compliant)
#ifdef _WIN32	
	{NET_EPROCLIM, "WSAEPROCLIM - too many processes"},
	{NET_SYSNOTREADY, "WSASYSNOTREADY - network subsystem is unavailable"},
	{NET_VERNOTSUPPORTED, "WSAVERNOTSUPPORTED - winsock.dll verison out of range"},
	{NET_NOTINITIALISED, "WSANOTINITIALISED - WSAStartup not yet performed"},
	{NET_NO_DATA, "WSANO_DATA - valid name, no data record of requested type"},
	{NET_EDISCON, "WSAEDISCON - graceful shutdown in progress"},
#endif	

	// extended winsock errors (corresponding to BSD h_errno)
	{NET_HOST_NOT_FOUND, "WSAHOST_NOT_FOUND - host not found"},
	{NET_TRY_AGAIN, "WSATRY_AGAIN - non-authoritative host not found"},
	{NET_NO_RECOVERY, "WSANO_RECOVERY - non-recoverable error"},
	{NET_NO_DATA, "WSANO_DATA - valid name, no data record of requested type"},
	{NET_NO_ADDRESS, "WSANO_ADDRESS - no address, look for MX record"},

	// XNetwork specific
	{NET_NOIMPL, "XNetwork - Function not implemented"},
	{NET_SOCKET_NOT_CREATED, "XNetwork - socket not yet created"},
	{0, 0} // sentinel
};

class CNetwork::CNetworkThread : public CrySimpleThread<>
{
public:
	CNetworkThread() : m_threadID(0) {}

	void Run()
	{
		ScopedSwitchToGlobalHeap useGlobalHeap;










		while (Get()->UpdateTick(true))
			;
	}

	void Cancel()
	{
		Get()->m_pSocketIOManager->PushUserMessage(-1);
	}

	uint32 m_threadID;
};

void CNetwork::SetNetworkThreadPriority(ENetwork_Multithreading_Mode threadingMode)
{

















}

class CNetwork::CBufferWakeups
{
public:
	CBufferWakeups( bool release, CNetwork * pNet ) : m_release(false), m_pNet(pNet) { m_pNet->m_bufferWakeups++; }
	~CBufferWakeups()
	{
		if (0 == --m_pNet->m_bufferWakeups)
			if (m_release)
				if (m_pNet->m_wokenUp)
				{
					m_pNet->WakeThread();
					m_pNet->m_wokenUp = false;
				}
	}

private:
	bool m_release;
	CNetwork * m_pNet;
};

CNetwork::CNetwork()
: m_bQuitting(false)
, m_pResolver(0)
, m_bufferWakeups(0)
, m_wokenUp(false)
, m_nextCleanup(0.0f)
, m_cleanupMember(0)
, m_multithreadedMode(NETWORK_MT_OFF)
, m_occasionalCounter(OCCASIONAL_TICKS)
, m_cpuCount(1)
, m_forceLock(true)
, m_isPbSvActive(false)
, m_isPbClActive(false)
{
	NET_ASSERT( !m_pThis );
	m_pThis = this;
	g_time = gEnv->pTimer->GetAsyncTime();

	m_allowMinimalUpdate=false;
	m_bOverideChannelTickToGoNow=false;

	for (int i=0; i<sizeof(m_inSync)/sizeof(*m_inSync); i++)
		m_inSync[i] = 0;

	SCOPED_GLOBAL_LOCK;

	if (gEnv->IsDedicated())
		CServerProfiler::Init();

	//Timur, Turn off testing.
	/*
	if (!CWhirlpoolHash::Test())
	{
		CryFatalError("Implementation error in Whirlpool hash");
	}
	*/

#if ENABLE_DEBUG_KIT
	m_pNetInspector = CNetworkInspector::CreateInstance();
#endif
	m_pServiceManager.reset( new CServiceManager );

#if NETWORK_HOST_MIGRATION
	m_hostMigrationListeners.reserve(10);
#endif

	m_vFastChannelLookup.reserve(16);
	m_vMembers.reserve(16);

//#if defined(WIN32) || defined(WIN64)
//	m_needSleeping = false;
//	m_sink.m_pNetwork = this;
//#endif
}

CNetwork::~CNetwork()
{
	NET_ASSERT( m_pThis == this );

	SyncWithGame( eNGS_WakeNetwork );
	SyncWithGame( eNGS_Shutdown );
	NET_ASSERT( m_vMembers.empty() );

	if (m_multithreadedMode!=NETWORK_MT_OFF)
	{
		m_pThread->Cancel();
		m_pThread->Stop();
		m_pThread->WaitForThread();
		m_pThread.reset();
	}


	// Make sure all the queues are empty otherwise they will flush and execute when destroyed
	// which can crash if something gets used that has already been destroyed.
	m_toGame.FlushEmpty();
	m_fromGame.FlushEmpty();
	m_fromGame_otherThreadQueue.FlushEmpty();
	m_intQueue.FlushEmpty();
	m_toGameLazyBuilding.FlushEmpty();
	m_toGameLazyProcessing.FlushEmpty();

#if ENABLE_DEBUG_KIT
	CDebugKit::Shutdown();
#endif

	m_pServiceManager.reset();

	// can safely delete singletons after this point
	// as all threads have been completed

#if ENABLE_DISTRIBUTED_LOGGER
	m_pLogger.reset();
#endif

#if ENABLE_DEBUG_KIT
	if (m_pNetInspector)
		CNetworkInspector::ReleaseInstance();
#endif

	SAFE_DELETE(m_pSocketIOManager);

#if defined(WIN32) || defined(XENON)
	WSACleanup();
#if USE_XBOXLIVE
	XNetCleanup();
#endif // USE_LIVE
#else
	//CSocketManager::releaseImpl();
	//delete &ISocketManagerImplementation::getInstance();
#endif // WIN32 || XENON

	SAFE_DELETE(m_pResolver);

	if (gEnv->IsDedicated())
		CServerProfiler::Cleanup();
}

bool CNetwork::AllSuicidal()
{
	for (VMembers::const_iterator iter = m_vMembers.begin(); iter != m_vMembers.end(); ++iter)
		if (!(*iter)->IsSuicidal())
			return false;
	return true;
}

void CNetwork::FastShutdown()
{
	if (m_bQuitting)
		return;

	SCOPED_GLOBAL_LOCK;

	m_bQuitting = true;
	FlushNetLog(true);
	m_pServiceManager.reset();

	ICryLobby* pLobby = GetLobby();

	if (pLobby)
	{
		for (uint32 i = 0; i < eCLS_NumServices; i++)
		{
			pLobby->Terminate((ECryLobbyService)i, eCLSO_All, NULL, NULL);
		}
	}
}

void CNetwork::SetCDKey(const char* key)
{
  m_CDKey = key;
}

void CNetwork::AddExecuteString( const string& toExec )
{
	if (IsPrimaryThread())
		gEnv->pConsole->ExecuteString(toExec.c_str());
	else
		m_consoleCommandsToExec.push_back(toExec);
}

// EvenBalance - M. Quinn
bool CNetwork::PbConsoleCommand(const char* command, int length)
{














		return true ;

}

// EvenBalance - M. Quinn
void CNetwork::PbCaptureConsoleLog(const char* output, int length)
{







}

// EvenBalance - M. Quinn
void CNetwork::PbServerAutoComplete(const char* command, int length)
{






}

// EvenBalance - M. Quinn
void CNetwork::PbClientAutoComplete(const char* command, int length)
{






}

#if defined(WIN32) || defined(WIN64)
#include <IPHlpApi.h>
#pragma comment(lib, "IPHlpApi.lib")
#endif

bool CNetwork::CNetworkConnectivityDetection::HasNetworkConnectivity()
{
	float detectionInterval = std::max(1.0f, CVARS.NetworkConnectivityDetectionInterval);
	// if we've received a packet recently, then assume that we still have connectivity
	if ((g_time - m_lastPacketReceived).GetSeconds() < detectionInterval)
		m_hasNetworkConnectivity = true;
	// otherwise, if we've not checked for some time, check again (also triggers the first time this routine is called normally)
	else if ((g_time - m_lastCheck).GetSeconds() > detectionInterval)
	{
		DetectNetworkConnectivity();
		m_lastCheck = g_time;
	}
	return m_hasNetworkConnectivity;
}

void CNetwork::CNetworkConnectivityDetection::DetectNetworkConnectivity()
{
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_NETWORK);

	std::vector<string> warnings;
	bool hadNetworkConnectivity = m_hasNetworkConnectivity;

	m_hasNetworkConnectivity = true;

#if defined(WIN32) || defined(WIN64)
	DWORD size = 0;
	GetInterfaceInfo(NULL, &size);

	std::vector<BYTE> buffer;
	buffer.resize(size);

	PIP_INTERFACE_INFO info = (PIP_INTERFACE_INFO)&buffer[0];
	if (NO_ERROR != GetInterfaceInfo(info, &size))
	{
		warnings.push_back(string().Format("[connectivity] Failed retrieving network interface table"));
		m_hasNetworkConnectivity = false;
	}
	else if (info->NumAdapters <= 0)
	{
		warnings.push_back(string().Format("[connectivity] No network adapters available"));
		m_hasNetworkConnectivity = false;
	}
	else
	{
		DWORD nconnected = info->NumAdapters;
		for (LONG i = 0; i < info->NumAdapters; ++i)
		{
			IP_ADAPTER_INDEX_MAP& adapter = info->Adapter[i];
			MIB_IFROW ifr; ZeroMemory(&ifr, sizeof(ifr));
			ifr.dwIndex = adapter.Index;
			if (NO_ERROR != GetIfEntry(&ifr))
			{
				warnings.push_back(string().Format("[connectivity] Failed retrieving network interface at index %lu", ifr.dwIndex));
				//m_hasNetworkConnectivity = false;
				--nconnected;
			}
			else if (ifr.dwOperStatus != MIB_IF_OPER_STATUS_OPERATIONAL)
			{
				warnings.push_back(string().Format("[connectivity] %s is not operational", ifr.bDescr));
				//m_hasNetworkConnectivity = false;
				--nconnected;
			}
		}
		m_hasNetworkConnectivity = (nconnected != 0);
	}
#else
	// TODO:
#endif

	if (hadNetworkConnectivity && !m_hasNetworkConnectivity)
		for (size_t i = 0; i < warnings.size(); ++i)
			NetWarning(warnings[i].c_str());
}

bool CNetwork::HasNetworkConnectivity()
{
	SCOPED_GLOBAL_LOCK;
	return m_detection.HasNetworkConnectivity();
}

bool CNetwork::Init( int ncpu )
{
	m_cpuCount = ncpu;

	m_gameTime = gEnv->pTimer->GetFrameStartTime();
	m_pMessageQueueConfig = CMessageQueue::LoadConfig("Scripts/Network/Scheduler.xml");
	if (!m_pMessageQueueConfig)
	{
		NetWarning( "Network Config loading failed: Scripts/Network/Scheduler.xml" );
	}
	m_schedulerVersion = 1;

	RunTests();

	if (!m_pResolver)
		m_pResolver = new CNetAddressResolver();

#if defined(WIN32) || defined(XENON) || defined(gringo)

#if USE_XBOXLIVE
	XNetStartupParams xnsp;
	memset(&xnsp, 0, sizeof(xnsp));
	xnsp.cfgSizeOfStruct = sizeof(XNetStartupParams);
	// If Live security ever needs turning off reinsert the line below.
	// Security doesn't appear to effect standard UDP data so cross platform LAN games still work with it enabled.
//	xnsp.cfgFlags = XNET_STARTUP_BYPASS_SECURITY;
	if( XNetStartup( &xnsp ) != 0 )
	{
		NetWarning( "XNetStartup() failed." );
		return false;
	}
#endif // USE_XBOXLIVE

	uint16 wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return false;
	}

	if (LOBYTE(wsaData.wVersion) != 2 ||
		HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
#if USE_XBOXLIVE
		XNetCleanup();
#endif // USE_XBOXLIVE
		return false;
	}
#endif // WIN32 || XENON || gringo









	int n=0;
	while (m_neNetErrors[n].sErrorDescription!='\0')
	{
		m_mapErrors[m_neNetErrors[n].nrErrorCode]=m_neNetErrors[n].sErrorDescription;
		n++;
	}

	if (!(m_pSocketIOManager = CreateSocketIOManager(m_cpuCount)))
		return false;
	CryLogAlways("[net] using %s socket io management", m_pSocketIOManager->GetName());

	LogNetworkInfo();

	InitPrimaryThread();
	InitFrameTypes();

#if LOCK_NETWORK_FREQUENCY
	SetMultithreadingMode( NETWORK_MT_PRIORITY_NORMAL );
#endif

#if ENABLE_DISTRIBUTED_LOGGER
	m_pLogger.reset( new CDistributedLogger );
#endif

	g_pFileDownloader = new CAutoConfigDownloader;

#define REGISTER_POLICY(cls) \
	extern void RegisterCompressionPolicy_##cls(); \
	RegisterCompressionPolicy_##cls(); \

	REGISTER_POLICY(CAdaptiveBoolPolicy);
	REGISTER_POLICY(CAdaptiveFloatPolicy);
	REGISTER_POLICY(CAdaptiveOrientationPolicy);
	REGISTER_POLICY(CAdaptiveUnitVec3Policy);
	REGISTER_POLICY(CAdaptiveVec3Policy);
	REGISTER_POLICY(CAdaptiveVelocityPolicy);
	REGISTER_POLICY(CBiggerOrSmallerPolicy);
	REGISTER_POLICY(CDefaultPolicy);
	REGISTER_POLICY(CEntityIdPolicy);
	REGISTER_POLICY(CSimpleEntityIdPolicy);
	REGISTER_POLICY(CFloatAsIntPolicy);
	REGISTER_POLICY(CJumpyPolicy);
	REGISTER_POLICY(CNoSendPolicy);
	REGISTER_POLICY(CQuantizedVec3Policy);
	REGISTER_POLICY(CRangedIntPolicy);
	REGISTER_POLICY(CRangedUnsignedIntPolicy);
	REGISTER_POLICY(CStringTablePolicy);
	REGISTER_POLICY(CTimePolicy);
	REGISTER_POLICY(TTableDirVec3);

#undef REGISTER_POLICY
	return true;
}

void DownloadConfig()
{
	g_pFileDownloader->TriggerRefresh();
}

void CNetwork::SetMultithreadingMode( ENetwork_Multithreading_Mode threadingMode )
{
	m_mutex.Lock();

//#ifdef CAFE
//	threadingMode=NETWORK_MT_OFF;
//	OSREPORT("[CAFE COMMENT] Network has been changed to Simple-Threading mode\n");
//#endif
	if (threadingMode != m_multithreadedMode)
	{
		//Threading is currently disabled - start and set prio
		if (m_multithreadedMode==NETWORK_MT_OFF)
		{
			ScopedSwitchToGlobalHeap globalHeap;
			m_pThread.reset( new CNetworkThread() );
			m_pThread->Start(0, "Network");

			SetNetworkThreadPriority(threadingMode);
		}
		//disable thread
		else if(threadingMode==NETWORK_MT_OFF)
		{
			m_pThread->Cancel();
			m_mutex.Unlock();
			m_pThread->WaitForThread();
			m_mutex.Lock();
			m_pThread.reset();
		}
		//just a priority change
		else
		{
			SetNetworkThreadPriority(threadingMode);
		}
		m_multithreadedMode = threadingMode;
	}
	m_mutex.Unlock();
}

void CNetwork::WakeThread()
{
#if !LOCK_NETWORK_FREQUENCY
	if (m_bufferWakeups)
		m_wokenUp = true;
	else
		m_pSocketIOManager->PushUserMessage(-2);
#endif
}

void CNetwork::BroadcastNetDump( ENetDumpType type )
{
	for (size_t i=0; i<m_vMembers.size(); i++)
	{
		INetworkMemberPtr pMember = m_vMembers[i];
		pMember->NetDump(type);
	}
}

//////////////////////////////////////////////////////////////////////////
INetNub * CNetwork::CreateNub( const char * address, IGameNub * pGameNub, 
															 IGameSecurity * pSecurity, 
															 IGameQuery * pGameQuery )
{
	SCOPED_GLOBAL_LOCK;
	TNetAddressVec addrs;
	if (address)
	{
		CNameRequestPtr pReq = GetResolver()->RequestNameLookup(address);
		pReq->Wait();
		if (pReq->GetResult(addrs) != eNRR_Succeeded)
		{
			CryLogAlways("Name resolution for '%s' failed", address);
			return NULL;
		}
//		GetResolver()->FromString( address, addrs );
	}
	else
	{
		SIPv4Addr addr; // defaults to 0,0
		addrs.push_back( TNetAddress( addr ) );
	}
	if (addrs.empty())
	{
		SAFE_RELEASE(pGameNub);
		return NULL;
	}

	//CryLog( "Resolved name as %s", GetResolver()->ToNumericString(addrs[0]).c_str() );
	CNetNub * pNub = new CNetNub( addrs[0], pGameNub, 
																pSecurity, pGameQuery );
	if (!pNub->Init(this))
	{
		NetWarning("Failed creating network nub at %s", address);
		delete pNub;
		return NULL;
	}

#if NETWORK_REBROADCASTER
	CCryRebroadcaster* pRebroadcaster = NULL;
	CCryLobby* pLobby = (CCryLobby*)CCryLobby::GetLobby();
	if (pLobby)
	{
		pRebroadcaster = pLobby->GetRebroadcaster();
	}

	if (pRebroadcaster != NULL)
	{
		// Rebroadcaster uses the first nub created (game server nub on the server and game client nub on subsequent clients)
		// This is necessary as these nubs are the only nubs that can have external communication (the game client nub that's
		// created on the server is for local datagrams only and will silently consume non-local traffic).
		pRebroadcaster->SetNetNub(pNub);
	}
#endif

	AddMember(pNub);
	CNetwork::Get()->WakeThread();
	return pNub;
}

ILanQueryListener * CNetwork::CreateLanQueryListener( IGameQueryListener * pGameQueryListener )
{
	SCOPED_GLOBAL_LOCK;
	CLanQueryListener * pLanQueryListener = new CLanQueryListener(pGameQueryListener);
	if (!pLanQueryListener->Init())
	{
		delete pLanQueryListener;
		return NULL;
	}

	AddMember( pLanQueryListener );
	CNetwork::Get()->WakeThread();
	return pLanQueryListener;
}

INetContext * CNetwork::CreateNetContext( IGameContext * pGameContext, uint32 flags )
{
	SCOPED_GLOBAL_LOCK;
	CNetContext * pContext = new CNetContext(pGameContext, flags);
	AddMember( pContext );
	CNetwork::Get()->WakeThread();
	return pContext;
}

const char *CNetwork::EnumerateError(NRESULT err)
{
	TErrorMap::iterator i = m_mapErrors.find(err);
	if (i != m_mapErrors.end())
	{
		return i->second;
	}
	return "Unknown";
}

void CNetwork::Release()
{
//	SCOPED_GLOBAL_LOCK;
	
//	if (m_nNetworkInitialized)
//		;

	delete this;
	m_pThis = 0;
}

void CNetwork::GetMemoryStatistics(ICrySizer *pSizer)
{
	// dont lock if we want to compare the heap with all allocated data
#if !defined(PS3_CRYSIZER_HEAP_TRAVERSAL)
	SCOPED_GLOBAL_LOCK;
#endif

#ifndef _LIB // Only when compiling as dynamic library
	{
		//SIZER_COMPONENT_NAME(pSizer,"Strings");
		//pSizer->AddObject( (this+1),string::_usedMemory(0) );
	}
	{
		SIZER_COMPONENT_NAME(pSizer,"STL Allocator Waste");
		CryModuleMemoryInfo meminfo;
		ZeroStruct(meminfo);
		CryGetMemoryInfoForModule( &meminfo );
		pSizer->AddObject( (this+2),(size_t)meminfo.STL_wasted );
	}
#endif

	SIZER_COMPONENT_NAME(pSizer, "CNetwork");

	if (!pSizer->Add(*this))
		return;

	pSizer->AddObject(m_neNetErrors,sizeof(m_neNetErrors));
	pSizer->AddContainer(m_vMembers);

	for (VMembers::iterator i = m_vMembers.begin(); i != m_vMembers.end(); ++i)
		(*i)->GetMemoryStatistics( pSizer );

	m_toGame.GetMemoryStatistics(pSizer);
	m_fromGame.GetMemoryStatistics(pSizer);
	m_intQueue.GetMemoryStatistics(pSizer);
	m_toGameLazyBuilding.GetMemoryStatistics(pSizer);
	m_toGameLazyProcessing.GetMemoryStatistics(pSizer);
	m_timer.GetMemoryStatistics(pSizer);

	{
		CryAutoLock<NetFastMutex> lk(m_fromGame_otherThreadLock);
		m_fromGame_otherThreadQueue.GetMemoryStatistics(pSizer);
	}

//	for (int i=0; i<eMMT_NUM_TYPES; i++)
//		m_mmm[i].GetMemoryStatistics(pSizer);

	m_compressionManager.GetMemoryStatistics(pSizer);

	m_pServiceManager->GetMemoryStatistics(pSizer);

	//m_pMessageQueueConfig->GetMemoryStatistics(pSizer);

//#if ( defined(WIN32) || defined(WIN64) ) && ( defined(_DEBUG) || defined(DEBUG) )
//	_CrtMemDumpAllObjectsSince(NULL);
//	_CrtMemState ms;
//	_CrtMemCheckpoint(&ms);
//	_CrtMemDumpStatistics(&ms);
//#endif
}

class CInSync
{
public:
	CInSync( int& inSync ) : m_inSync(inSync) 
	{ 
		ASSERT_PRIMARY_THREAD;
		++m_inSync; 
	}
	~CInSync() 
	{ 
		ASSERT_PRIMARY_THREAD;
		--m_inSync; 
	}
private:
	int& m_inSync;
};

void CNetwork::DoSyncWithGameMinimal()
{
		m_toGame.Flush(true);					// Keep network and game talking - but don't try to process lazy queue - we are actually in the middle of its flush
		m_fromGame.Flush(true);
		
		ICryLobby* pLobby = GetLobby();
		if (pLobby)
		{
			ECryLobbyError error = pLobby->ProcessEvents();

			if (error != eCLE_Success)
			{
				NetLog("Minimal: Error running pLobby->ProcessEvents (%d)",error);
			}
		}
}

//////////////////////////////////////////////////////////////////////////
void CNetwork::SyncWithGame(ENetworkGameSync type)
{
	if (type == eNGS_AllowMinimalUpdate)
	{
		m_allowMinimalUpdate=true;
		return;
	}
	if (type == eNGS_DenyMinimalUpdate)
	{
		m_allowMinimalUpdate=false;
		return;
	}
	if (type == eNGS_MinimalUpdateForLoading)
	{
		if (m_allowMinimalUpdate)
		{
			SCOPED_GLOBAL_LOCK;
			DoSyncWithGameMinimal();
		}
		return;
	}
	if (type == eNGS_WakeNetwork)
	{
#if LOCK_NETWORK_FREQUENCY
		m_pSocketIOManager->ForceNetworkStart();
#endif
		return;
	}
	if (type == eNGS_ForceChannelTick)
	{
		m_bOverideChannelTickToGoNow=true;
		return;
	}

	//#if defined(WIN32) || defined(WIN64)
	//	// HACK: to make the WMI thread responsive on single core
	//	if (type == eNGS_FrameEnd && m_cpuCount < 2 && m_needSleeping)
	//		Sleep(1);
	//#endif

	static CThreadProfilerEvent evt_start("net_startframe");
	CThreadProfilerEvent::Instance show_evt_start(evt_start, type == eNGS_FrameStart);

	FUNCTION_PROFILER( GetISystem(), PROFILE_NETWORK );

#if _DEBUG && defined(USER_craig)
	//SCOPED_ENABLE_FLOAT_EXCEPTIONS;
#endif

	if (m_bQuitting)
	{
		return;
	}

	int totSync = 0;
	for (int i=0; i<sizeof(m_inSync)/sizeof(*m_inSync); i++)
	{
		totSync += m_inSync[i];
	}

	switch (type)
	{
	case eNGS_Shutdown:
		if (totSync)
		{
			CryFatalError("CNetwork::SyncWithGame(eNGS_Shutdown) called recursively");
		}
		break;
	case eNGS_FrameStart:
	case eNGS_FrameEnd:
		if (totSync && totSync != (m_inSync[eNGS_Shutdown] + m_inSync[eNGS_NUM_ITEMS+1]))
		{
			return;
		}
		break;
	}

	{
		CTimeValue startTime = gEnv->pTimer->GetAsyncTime();

		SCOPED_GLOBAL_LOCK;
		CBufferWakeups bufferWakeups(type == eNGS_FrameEnd, this);
		CInSync inSync(m_inSync[type]);
		DoSyncWithGame(type);

		CTimeValue middleTime = gEnv->pTimer->GetAsyncTime();

		ECryLobbyError error = eCLE_Success;
		///// Moved here, need to ensure we have the global lock due to memory allocations..etc in the lobby
		ICryLobby* pLobby = GetLobby();
		if (pLobby)
		{
			error = pLobby->ProcessEvents();

			if (error != eCLE_Success)
			{
				NetLog("Error running pLobby->ProcessEvents (%d)",error);
			}
		}

		CTimeValue endTime = gEnv->pTimer->GetAsyncTime();

		int64 lobbyTick = (endTime - middleTime).GetMilliSecondsAsInt64();
		int64 normalTick = (middleTime - startTime).GetMilliSecondsAsInt64();
		int64 total = lobbyTick + normalTick;

		if (total > 50LL)
		{
			NetLog("CNetwork::SyncWithGame() took a long time %lld, normal=%lld, lobby=%lld, gotLobbyLock=%s", (long long)total, (long long)normalTick, (long long)lobbyTick, error == eCLE_Success ? "true" : "false");
		}
	}

	if (!m_inSync[eNGS_NUM_ITEMS+1])
	{
		CInSync inSyncFlush(m_inSync[eNGS_NUM_ITEMS+1]);
		m_toGameLazyProcessing.Flush(false);

	}
}

#if ENABLE_OBJECT_COUNTING
struct SObjCntDisplay
{
	const char * name;
	int n;
	bool operator<( const SObjCntDisplay& r ) const
	{
		return n > r.n;
	}
};
static void DumpObjCnt()
{
	SObjCntDisplay temp;
	std::vector<SObjCntDisplay> v;
#define COUNTER(cnt) temp.n = g_objcnt.cnt.QuickPeek(); if (temp.n) { temp.name = #cnt; v.push_back(temp); }
#include "objcnt_defs.h"
#undef COUNTER

	std::sort(v.begin(), v.end());

	if (ITextModeConsole * pTMC = gEnv->pSystem->GetITextModeConsole())
	{
		char buf[256];
		int y = 20;

		IMemoryManager::SProcessMemInfo memInfo;
		GetISystem()->GetIMemoryManager()->GetProcessMemInfo( memInfo );
		float kils = memInfo.WorkingSetSize / 1024.0f;
		sprintf(buf, "MEM: %.2f kilobyte", kils);
		pTMC->PutText(0, y++, buf);

		for (std::vector<SObjCntDisplay>::iterator it = v.begin(); it != v.end(); ++it)
		{
			sprintf(buf, "%d", it->n);
			pTMC->PutText(0, y, buf);
			pTMC->PutText(10, y++, it->name);
		}
	}
};
#endif
//////////////////////////////////////////////////////////////////////////
void CNetwork::GetPerformanceStatistics(SNetworkPerformance *pSizer)
{
	pSizer->m_nNetworkSync = m_networkPerformance.m_nNetworkSync;
}


//////////////////////////////////////////////////////////////////////////
void CNetwork::DoSyncWithGame( ENetworkGameSync type )
{
	bool continuous = false;

//

	switch (type)
	{
	case eNGS_FrameStart:
		{
			PerformanceGuard guard(&m_networkPerformance, 0);
			FRAME_PROFILER("Network:FrameStart", gEnv->pSystem, PROFILE_NETWORK);
			if (gEnv->IsDedicated())
				g_pFileDownloader->Update();
			if (!gEnv->IsEditor())
				m_gameTime = gEnv->pTimer->GetFrameStartTime();
			else
				m_gameTime = gEnv->pTimer->GetAsyncCurTime();
			//NetQuickLog(false, 0, "Local: %f", m_gameTime.GetSeconds());
			FlushNetLog(false);
			m_toGame.Flush(true);
			if (!m_inSync[eNGS_NUM_ITEMS+1])
				m_toGameLazyBuilding.Swap(m_toGameLazyProcessing);

#if ENABLE_DEBUG_KIT
			if (CNetCVars::Get().PerfCounters)
			{
				if (ITextModeConsole * pTMC = gEnv->pSystem->GetITextModeConsole())
				{
					int y = 0;
					char buf[256];
#define DUMP_COUNTER(name) sprintf(buf, #name ": %d (%d)", g_systemBranchCounters.name, g_systemBranchCounters.name - g_lastSystemBranchCounters.name); pTMC->PutText(0, y++, buf)
					DUMP_COUNTER(updateTickMain);
					DUMP_COUNTER(updateTickSkip);
					DUMP_COUNTER(iocpReapSleep);
					DUMP_COUNTER(iocpReapSleepCollect);
					DUMP_COUNTER(iocpReapImmediate);
					DUMP_COUNTER(iocpReapImmediateCollect);
					DUMP_COUNTER(iocpIdle);
					DUMP_COUNTER(iocpBackoffCheck);
					DUMP_COUNTER(iocpForceSync);
#undef DUMP_COUNTER
					g_lastSystemBranchCounters = g_systemBranchCounters;
				}
			}
#endif

#if ENABLE_OBJECT_COUNTING
			DumpObjCnt();
#endif
		}
		break;
	case eNGS_FrameEnd:
		{
			FRAME_PROFILER("Network:FrameEnd", gEnv->pSystem, PROFILE_NETWORK);
			PerformanceGuard guard(&m_networkPerformance, m_networkPerformance.m_nNetworkSync);

			//NetQuickLog( gEnv->IsDedicated(), 0, "locks/frame: %d", g_lockCount );
			g_lockCount = 0;

			FlushNetLog(true);
			m_fromGame.Flush(true);
			{
				CryAutoLock<NetFastMutex> lk(m_fromGame_otherThreadLock);
				m_fromGame_otherThreadQueue.Flush(true);
			}
			CMementoMemoryManager::DebugDraw();

#if ENABLE_DEBUG_KIT
			CDebugKit::Get().Update();
#endif
#if STATS_COLLECTOR_INTERACTIVE
			GetStats()->InteractiveUpdate();
#endif

			if (CServerProfiler::ShouldSaveAndCrash())
			{
				gEnv->pConsole->ExecuteString("SaveLevelStats");
				_exit(0);
			}
		}
		break;
	case eNGS_Shutdown:
		FlushNetLog(true);
		m_toGame.Flush(true);
		m_fromGame.Flush(true);
		{
			CryAutoLock<NetFastMutex> lk(m_fromGame_otherThreadLock);
			m_fromGame_otherThreadQueue.Flush(true);
		}
		continuous = true;
		break;
	case eNGS_Shutdown_Clear:
		FlushNetLog(true);
		m_toGame.FlushEmpty();
		m_fromGame.FlushEmpty();
		{
			CryAutoLock<NetFastMutex> lk(m_fromGame_otherThreadLock);
			m_fromGame_otherThreadQueue.FlushEmpty();
		}
		continuous = true;
		break;
	}

	if (!continuous)
	{
		UpdateLoop(type);
	}
	else while (true)
	{
		CInSync inSync(m_inSync[eNGS_NUM_ITEMS]);

		bool emptied = false;
		bool suicidal = false;
		{
			m_toGame.Flush(true);
			UpdateLoop( type );
			emptied = m_vMembers.empty();
			if (!emptied)
				suicidal = AllSuicidal();
			m_fromGame.Flush(true);
			m_toGame.Flush(true);
			{
				CryAutoLock<NetFastMutex> lk(m_fromGame_otherThreadLock);
				m_fromGame_otherThreadQueue.Flush(true);
			}
		}
		if (continuous)
			if (!emptied)
				if (suicidal)
				{
					{
						CInSync inSyncFlush(m_inSync[eNGS_NUM_ITEMS+1]);
						if (m_multithreadedMode!=NETWORK_MT_OFF)
							m_mutex.Unlock();
						m_toGameLazyProcessing.Flush(false);
						if (m_multithreadedMode!=NETWORK_MT_OFF)
							m_mutex.Lock();
						m_toGameLazyBuilding.Swap(m_toGameLazyProcessing);
					}
					DoSyncWithGame(eNGS_FrameStart);
					DoSyncWithGame(eNGS_FrameEnd);
#if LOCK_NETWORK_FREQUENCY
					m_pSocketIOManager->ForceNetworkStart();
#endif
					continue;
				}
		break;
	}
#if ENABLE_DISTRIBUTED_LOGGER
	if (m_pLogger.get())
		m_pLogger->Update( g_time );
#endif
#if ENABLE_DEBUG_KIT
	if (m_cvars.NetInspector)
		if (m_pNetInspector)
			m_pNetInspector->Update();
#endif
}

void CNetwork::UpdateLoop( ENetworkGameSync type )
{
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_NETWORK);

	if (type == eNGS_Shutdown)
	{
		return;
	}
	else if (type == eNGS_FrameEnd)
	{
		for (size_t i=0; i<m_vMembers.size(); i++)
		{
			INetworkMemberPtr pMember = m_vMembers[i];
			if (pMember->IsDead())
			{
				m_vMembers.erase( m_vMembers.begin() + i );
				i --;
			}
			else
			{
				ENSURE_REALTIME;
				pMember->SyncWithGame(type);
			}
		}
	}
	else
	{
		for (size_t i=0; i<m_vMembers.size(); i++)
		{
			ENSURE_REALTIME;
			// Prevent members that die during the frame from being sync'ed (can happen
			// during host migration as this can occur at any point in the frame)
			INetworkMemberPtr pMember = m_vMembers[i];
			if (!pMember->IsDead())
			{
				pMember->SyncWithGame(type);
			}
		}
	}























	if (m_multithreadedMode==NETWORK_MT_OFF && type == eNGS_FrameEnd)
	{
		UpdateTick(false);
	}
}

bool CNetwork::UpdateTick( bool mt )
{
	float waitTime = 0.0f;

	ITimer * pTimer = gEnv->pTimer;
	bool mustLock = m_forceLock;
	m_forceLock = false;
	CTimeValue now = pTimer->GetAsyncTime();
	mustLock |= ((now - m_lastUpdateLock).GetMilliSeconds() > 15);

	bool isLocked = false;
	if (mustLock)
	{
		m_mutex.Lock();
		isLocked = true;
		m_lastUpdateLock = pTimer->GetAsyncTime();
	}
	else
	{
		isLocked = m_mutex.TryLock();
		m_lastUpdateLock = now;
	}
		
	if (isLocked)
	{
		FUNCTION_PROFILER(gEnv->pSystem, PROFILE_NETWORK);

		g_systemBranchCounters.updateTickMain ++;

		waitTime = (TIMER.Update() - g_time).GetSeconds();

//#if CHANNEL_TIED_TO_NETWORK_TICK
		if (gEnv->bMultiplayer && m_bOverideChannelTickToGoNow)
		{
			// Tick the channels
			const uint32 numMembers = m_vMembers.size();
			for (uint32 i = 0; i < numMembers; ++ i)
			{
				INetworkMemberPtr pMember = m_vMembers[i];
				if (pMember->UpdateOrder == eNMUO_Nub)
				{
					pMember->TickChannels(now);
				}
			}
			m_bOverideChannelTickToGoNow=false;
		}
//#endif

		m_pThis->m_intQueue.Flush(true);

		if (!m_vMembers.empty() && g_time >= m_nextCleanup)
		{
			INetworkMemberPtr pMember = m_vMembers[m_cleanupMember % m_vMembers.size()];
			if (!pMember->IsDead())
				pMember->PerformRegularCleanup();
			m_cleanupMember ++;
		}


#define WAIT_TIME_MIN   (1.0f) //in milliseconds

		waitTime *= 1000.0f;

		if (waitTime > 600.0f*1000)
			waitTime = 600.0f*1000;
		else if (waitTime < WAIT_TIME_MIN)
			waitTime = WAIT_TIME_MIN;

		if (!mt)
			waitTime = 0;

		m_mutex.Unlock();
	}
	else
	{
		waitTime = 1;
		g_systemBranchCounters.updateTickSkip ++;
	}

	bool ret = true;
	bool didWorkInPoll = false;



	int pollres = m_pSocketIOManager->Poll(waitTime, didWorkInPoll);
	if (pollres <= -666)
		;
	else switch (pollres)
	{
	case -1:
		ret = false;
		break;
	case 1:
	case 2:
	case 3:
	case 4:
	case -2:
		break;
	default:
		NetWarning("Unhandled poll result %d", pollres);
		break;
	}
	m_forceLock = !didWorkInPoll;

	return ret;
}

namespace
{
	struct CompareMembers
	{
		bool operator()( INetworkMemberPtr p1, INetworkMemberPtr p2 ) const
		{
			return p1->UpdateOrder < p2->UpdateOrder;
		}
	};
}

void CNetwork::AddMember( INetworkMemberPtr pMember )
{
	m_vMembers.push_back(pMember);



	std::stable_sort(m_vMembers.begin(), m_vMembers.end(), CompareMembers());

}

const char * CNetwork::GetHostName()
{
	static char buf[256];









	gethostname(buf, 256);

	return buf;
}

void CNetwork::LogNetworkInfo()
{
#if defined(WIN32) || defined(LINUX) && !defined(gringo)

	uint32 i; 
	char	buf[256];
	struct	hostent *hp;

	if(!gethostname(buf, sizeof(buf))) 
	{
		hp = gethostbyname(buf);
		if (hp) 
		{			
			CryLogAlways("network hostname: %s",hp->h_name);
			
			i = 0;

			while(hp->h_aliases[i]) 
			{
				CryLogAlways("  alias: %s\n", hp->h_aliases[i]);
				i++;
			}

			i = 0;

			while(hp->h_addr_list[i])
			{
				sockaddr_in temp;

				memcpy(&(temp.sin_addr), hp->h_addr_list[i], hp->h_length);

#if defined(LINUX)
				const in_addr_windows *pin_addr_win = reinterpret_cast<const in_addr_windows*>(&temp.sin_addr);
				CryLog("  ip:%d.%d.%d.%d",		//  port:%d  family:%x",	
					(int)(pin_addr_win->S_un.S_un_b.s_b1),
					(int)(pin_addr_win->S_un.S_un_b.s_b2),
					(int)(pin_addr_win->S_un.S_un_b.s_b3),
					(int)(pin_addr_win->S_un.S_un_b.s_b4));
#else
				CryLogAlways("  ip:%d.%d.%d.%d",		//  port:%d  family:%x",	
					(int)(temp.sin_addr.S_un.S_un_b.s_b1),
					(int)(temp.sin_addr.S_un.S_un_b.s_b2),
					(int)(temp.sin_addr.S_un.S_un_b.s_b3),
					(int)(temp.sin_addr.S_un.S_un_b.s_b4));
			//		(int)temp.sin_port,(unsigned int)temp.sin_family);
#endif
				i++;
			}		
		}
	}
#endif //#if defined(WIN32) || defined(LINUX)
}

const char * GetTimestampString()
{
	static char buffer[512];

#ifdef WIN32
	SYSTEMTIME st;
	GetSystemTime(&st);
	sprintf(buffer, "%.2d:%.2d:%.2d.%.3d", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
	return buffer;
#else
	struct tm st;
# if defined(LINUX)
	struct timeval now;
	gettimeofday(&now, NULL);
	if( gmtime_r(&now.tv_sec, &st) != NULL )
	{
		sprintf(buffer, "%.2d:%.2d:%.2d.%.3d", st.tm_hour, st.tm_min, st.tm_sec, (int)(now.tv_usec+500)/1000);
		return buffer;
	}







# endif
	return "";
#endif

}

INetworkServicePtr CNetwork::GetService( const char * name )
{
  if( m_pServiceManager.get() )
	  return m_pServiceManager->GetService(name);
  return NULL;
}

IRemoteControlSystem* CNetwork::GetRemoteControlSystemSingleton()
{
	return &CRemoteControlSystem::GetSingleton();
}

ISimpleHttpServer* CNetwork::GetSimpleHttpServerSingleton()
{
	return &CSimpleHttpServer::GetSingleton();
}

ICryLobby* CNetwork::GetLobby()
{
	return CCryLobby::GetLobby();
}

void CNetwork::ReloadScheduler()
{
	ASSERT_GLOBAL_LOCK;
	
	CMessageQueue::CConfig * old = m_pMessageQueueConfig;
	m_pMessageQueueConfig = CMessageQueue::LoadConfig(PathUtil::GetGameFolder() + "/Scripts/Network/Scheduler.xml");
	if (m_pMessageQueueConfig)
	{
		CMessageQueue::DestroyConfig(old);
		m_schedulerVersion ++;
	}
	else
		m_pMessageQueueConfig = old;
}

void CNetwork::SetNetGameInfo( SNetGameInfo info )
{
	SCOPED_GLOBAL_LOCK;
	m_gameInfo = info;
}

SNetGameInfo CNetwork::GetNetGameInfo()
{
	ASSERT_GLOBAL_LOCK;
	return m_gameInfo;
}

int CNetwork::RegisterForFastLookup(CNetChannel * pChannel)
{
	size_t i;
	for (i=0; i<m_vFastChannelLookup.size(); i++)
	{
		if (!m_vFastChannelLookup[i])
		{
			m_vFastChannelLookup[i] = pChannel;
			return i;
		}
	}
	m_vFastChannelLookup.push_back(pChannel);
	return i;
}

void CNetwork::UnregisterFastLookup(int id)
{
	NET_ASSERT(id>=0);
	NET_ASSERT(id<(int)m_vFastChannelLookup.size());
	NET_ASSERT(m_vFastChannelLookup[id]);
	m_vFastChannelLookup[id] = 0;
}

CNetChannel * CNetwork::GetChannelByFastLookupId(int id)
{
	NET_ASSERT(id>=0);
	if (id<(int)m_vFastChannelLookup.size())
		return m_vFastChannelLookup[id];
	else
		return 0;
}

void CEnsureRealtime::Failed()
{
	DEBUG_BREAK;
	NET_ASSERT(!"REALTIME");
}

























// unit tests


struct ImprovedTest
{
	uint32 nValue; 
	uint32 nLeft; 
	uint32 nRight; 
	uint32 nHeight; 
	uint32 nRange;
	uint8  nInRangePercentage;
	uint8  nMaxBits;
};


struct ITestPolyQueue 
{
	virtual ~ITestPolyQueue(){}
	virtual void Poo() = 0;
};
CPolymorphicQueue<ITestPolyQueue> testq;
template <int N>
struct CTestPolyQueue : public ITestPolyQueue
{
	CTestPolyQueue(int i) : m_val(i) {}
	int m_val;
	int padding[N*100];
	void Poo() 
	{
		//		CryLogAlways("%d", m_val);
		if (rand()>RAND_MAX/2)
			testq.Add(CTestPolyQueue(m_val + 10000000));
	}
};
void TestPolyQueue( ITestPolyQueue * p )
{
	p->Poo();
}

bool TestStringToKey( const char * x, uint32 n )
{
	uint32 m;
	if (!StringToKey(x, m))
		return false;
	if (m != n)
		return false;
	if (KeyToString(n) != x)
		return false;
	return true;
}

extern void TestTableDirVec3();

class CStressTestMMM
{
public:
	void Run(int iters)
	{
		for (int i=0; i<iters; i++)
		{
			bool alloc = rand() > RAND_MAX/3;
			bool resize = false;
			if (m_alloced.size() > 15000)
				alloc = false;
			if (m_alloced.empty())
				alloc = true;
			if (alloc && !m_alloced.empty() && rand() < RAND_MAX/2)
				resize = true;
			VerifyAll();
			size_t sz = rand() % 2070;
			int which = m_alloced.empty()? 0 : rand() % m_alloced.size();
			if (alloc)
			{
				if (resize)
				{
					std::map<TMemHdl, size_t>::iterator it = m_alloced.begin();
					for (int j=0; j<which; j++)
						++it;
					m_mmm.ResizeHdl(it->first, sz);
					it->second = sz;
					Init(it->first);
					//					VerifyAll();
				}
				else
				{
					TMemHdl hdl = m_mmm.AllocHdl(sz);
					m_alloced.insert( std::make_pair(hdl, sz) );
					Init(hdl);
					//					VerifyAll();
				}
			}
			else
			{
				std::map<TMemHdl, size_t>::iterator it = m_alloced.begin();
				for (int j=0; j<which; j++)
					++it;
				m_mmm.FreeHdl(it->first);
				m_alloced.erase(it);
				//				VerifyAll();
			}
		}
	}

private:
	CMementoMemoryManager m_mmm;
	std::map<TMemHdl, size_t> m_alloced;

	void Init( TMemHdl hdl )
	{
		std::map<TMemHdl, size_t>::iterator it = m_alloced.find(hdl);
		NET_ASSERT(it != m_alloced.end());
		NET_ASSERT(m_mmm.GetHdlSize(hdl) == it->second);
		uint8 * p = (uint8 *)m_mmm.PinHdl(hdl);
		for (uint32 i=0; i<it->second; i++)
		{
			uint8 b = i;
			p[i] = b;
		}
		Verify(hdl);
	}

	void VerifyAll()
	{
		for (std::map<TMemHdl, size_t>::iterator it = m_alloced.begin(); it != m_alloced.end(); ++it)
			Verify(it->first);
	}

	void Verify( TMemHdl hdl )
	{
		std::map<TMemHdl, size_t>::iterator it = m_alloced.find(hdl);
		NET_ASSERT(it != m_alloced.end());
		NET_ASSERT(m_mmm.GetHdlSize(hdl) == it->second);
		uint8 * p = (uint8 *)m_mmm.PinHdl(hdl);
		for (uint32 i=0; i<it->second; i++)
		{
			uint8 b = i;
			NET_ASSERT( p[i] == b );
		}
	}
};

struct SAlphabetTestOp
{
	uint32 what;
	uint32 value;
	uint32 buffer;
};

void CNetwork::RunTests()
{












































































































































































































	/*
	CDefaultStreamAllocator dsa;
	for (size_t i=0; i<10000000u; i++)
	{
	CCommOutputStream os(&dsa, 1024);
	std::vector<ImprovedTest> check;
	for (size_t j=0; j<500; j++)
	{
	ImprovedTest t;
	t.nHeight = rand() % 2048 + 1;
	t.nMaxBits = rand() % 22 + 2;
	t.nRange = (1 << t.nMaxBits)-1;
	t.nInRangePercentage = rand()%98 + 1;
	t.nLeft = rand() % t.nRange;
	t.nRight = rand() % t.nRange;
	if (t.nLeft > t.nRight)
	std::swap(t.nLeft, t.nRight);
	t.nValue = rand() % t.nRange;
	check.push_back(t);
	SquarePulseProbabilityWriteImproved( os, t.nValue, t.nLeft, t.nRight, t.nHeight, t.nRange, t.nInRangePercentage, t.nMaxBits );
	}
	os.Flush();
	CCommInputStream is(os.GetPtr(), os.GetOutputSize());
	for (size_t j=0; j<check.size(); j++)
	{
	ImprovedTest& t = check[j];
	uint32 x = SquarePulseProbabilityReadImproved( is, t.nLeft, t.nRight, t.nHeight, t.nRange, t.nInRangePercentage, t.nMaxBits );
	while (x != t.nValue)
	{
	CryFatalError("Bad Format");
	uint8 buf[512];
	CCommOutputStream os2(buf, 512);
	SquarePulseProbabilityWriteImproved( os2, t.nValue, t.nLeft, t.nRight, t.nHeight, t.nRange, t.nInRangePercentage, t.nMaxBits );
	os2.Flush();
	CCommInputStream is2(buf, os2.GetOutputSize());
	uint32 x2 = SquarePulseProbabilityReadImproved( is2, t.nLeft, t.nRight, t.nHeight, t.nRange, t.nInRangePercentage, t.nMaxBits );
	x = x2;
	}
	}
	}
	*/

	/*
	IVoiceEncoder * pV = CVoiceManager::CreateEncoder( GetVoiceCodec() );
	IVoiceDecoder * pW = CVoiceManager::CreateDecoder( GetVoiceCodec() );
	if (pV && pW)
	{
	int frameSize = pV->GetFrameSize();

	std::vector<short> buffer(frameSize);
	for (int i=0; i<frameSize; i++)
	buffer[i] = 8000 * sin(0.5 * i);
	uint8 temp[1024];

	pW->DecodeFrame( pV->EncodeFrame( frameSize, &buffer[0], 1024, temp ), temp, frameSize, &buffer[0] );

	pV->Release();
	}
	*/

}

CNetChannel * CNetwork::FindFirstClientChannel()
{
	for (VMembers::const_iterator iter = m_vMembers.begin(); iter != m_vMembers.end(); ++iter)
		if (CNetChannel * pChan = (*iter)->FindFirstClientChannel())
			return pChan;
	return 0;
}

// NOTE: the following functions cannot call isPbClSvEnabled() function directly, since they tend to
// load PB dll's if not loaded, and that's not something we want (what we are doing is load PB dll
// only during PB enabled multiplayer sessions)

bool CNetwork::IsPbClEnabled()
{
	return m_isPbClActive;
}

bool CNetwork::IsPbSvEnabled()
{
	return m_isPbSvActive;
}

void CNetwork::StartupPunkBuster(bool server)
{
















}

void CNetwork::CleanupPunkBuster()
{





}

bool CNetwork::IsPbInstalled()
{



	return false;

}

#if NET_PROFILE_ENABLE
bool CNetwork::NpGetChildFromCurrent(const char *name, SNetProfileStackEntry **entry, bool rmi)
{
	return netProfileGetChildFromCurrent(name, entry, rmi);
}

void CNetwork::NpRegisterBeginCall(const char *name, SNetProfileStackEntry** entry, float budget, bool rmi)
{
	netProfileRegisterBeginCall(name, entry, budget, rmi);
}

void CNetwork::NpBeginFunction(SNetProfileStackEntry* entry, bool read)
{
	netProfileBeginFunction(entry, read);
}

void CNetwork::NpEndFunction()
{
	netProfileEndFunction();
}

bool CNetwork::NpIsInitialised()
{
	return netProfileIsInitialised();
}

SNetProfileStackEntry* CNetwork::NpGetNullProfile()
{
	return netProfileGetNullProfile();
}
#else
bool CNetwork::NpGetChildFromCurrent(const char *name, SNetProfileStackEntry **entry, bool rmi){ return false; }
void CNetwork::NpRegisterBeginCall(const char *name, SNetProfileStackEntry** entry, float budget, bool rmi) {}
void CNetwork::NpBeginFunction(SNetProfileStackEntry* entry, bool read){}
void CNetwork::NpEndFunction(){}
bool CNetwork::NpIsInitialised(){ return false; }
SNetProfileStackEntry* CNetwork::NpGetNullProfile(){ return NULL; }
#endif
 
void CNetwork::GetBandwidthStatistics(SBandwidthStats *pStats)
{
#if NET_MINI_PROFILE
	pStats->m_numPacketsSent = g_socketBandwidth.totalNumPackets;
	pStats->m_socketBandwidthSent = g_socketBandwidth.totalBandwidthSent;
	pStats->m_socketBandwidthRecvd = g_socketBandwidth.totalBandwidthRecvd;
#endif
}

void CNetwork::GetDebugStats(SNetworkDebugStats* const pDebugStats)
{
#ifdef ENABLE_PROFILING_CODE
	for (size_t i=0; i<m_vMembers.size(); i++)
	{
		INetworkMemberPtr pMember = m_vMembers[i];
		pMember->GetDebugStats(pDebugStats);
	}
#endif // #ifdef ENABLE_PROFILING_CODE
}

bool CNetwork::DecodeAddress(const TNetAddress& address, uint32* pIP, uint16* pPort)
{
	bool decoded = true;

	const SIPv4Addr* pIPv4Addr = address.GetPtr<SIPv4Addr>();
	if (pIPv4Addr != NULL)
	{
		*pIP = pIPv4Addr->addr;
		*pPort = pIPv4Addr->port;
	}
	else
	{
		const TLocalNetAddress* pLocalAddr = address.GetPtr<TLocalNetAddress>();
		if (pLocalAddr != NULL)
		{
			*pIP = LOOPBACK_ADDRESS;
			*pPort = *pLocalAddr;
		}
		else
		{
			*pIP = *pPort = 0;
			decoded = false;
		}
	}

	return decoded;
}

void CNetwork::DecodeAddress(uint32 ip, uint16 port, char* ipstring, bool ignorePort)
{
	if (ignorePort)
	{
		sprintf(ipstring, "%i.%i.%i.%i", ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff), (ip & 0xff));
	}
	else
	{
		sprintf(ipstring, "%i.%i.%i.%i:%i", ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff), (ip & 0xff), port);
	}
}

void CNetwork::DecodeAddress(const TNetAddress& address, char* ipstring, bool ignorePort)
{
	uint32 ip;
	uint16 port;

	if (DecodeAddress(address, &ip, &port))
	{
		DecodeAddress(ip, port, ipstring, ignorePort);
	}
	else
	{
		sprintf(ipstring, "<UNKNOWN>");
	}
}


/////////////////////////////////////////////////////////////////////////////
// Rebroadcaster
bool CNetwork::IsRebroadcasterEnabled(void) const
{
	bool enabled = false;

	CCryRebroadcaster* pRebroadcaster = NULL;
	CCryLobby* pLobby = (CCryLobby*)CCryLobby::GetLobby();
	if (pLobby)
	{
		pRebroadcaster = pLobby->GetRebroadcaster();
	}

	if (pRebroadcaster)
	{
		enabled = pRebroadcaster->IsEnabled();
	}

	return enabled;
}

void CNetwork::AddRebroadcasterConnection(INetChannel* pChannel, TNetChannelID channelID)
{
	CCryRebroadcaster* pRebroadcaster = NULL;
	CCryLobby* pLobby = (CCryLobby*)CCryLobby::GetLobby();
	if (pLobby)
	{
		pRebroadcaster = pLobby->GetRebroadcaster();
	}

	if (pRebroadcaster)
	{
		pRebroadcaster->AddConnection(pChannel, channelID);
	}
}
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Host Migration
void CNetwork::EnableHostMigration(bool bEnabled)
{
#if NETWORK_HOST_MIGRATION
	CNetCVars::Get().netAutoMigrateHost = (bEnabled) ? 1 : 0;
#endif
}

bool CNetwork::IsHostMigrationEnabled(void)
{
	bool enabled = false;
#if NETWORK_HOST_MIGRATION
	enabled = (CNetCVars::Get().netAutoMigrateHost != 0);
#endif
	return enabled;
}

void CNetwork::TerminateHostMigration(CrySessionHandle gh)
{
	ICryLobby* pLobby = CCryLobby::GetLobby();
	if (pLobby)
	{
		ICryMatchMaking* pMatchMaking = pLobby->GetMatchMaking();
		if (pMatchMaking)
		{
			pMatchMaking->TerminateHostMigration(gh);
		}
	}
}

void CNetwork::AddHostMigrationEventListener(IHostMigrationEventListener* pListener, const char* pWho)
{
#if NETWORK_HOST_MIGRATION
	for (THostMigrationEventListenerVector::iterator it = m_hostMigrationListeners.begin(); it != m_hostMigrationListeners.end(); ++it)
	{
		if (it->m_pListener == pListener)
		{
			// The listener is already registered
			return;
		}
	}

	SHostMigrationEventListenerInfo info(pListener, pWho);
	m_hostMigrationListeners.push_back(info);
#endif
}

void CNetwork::RemoveHostMigrationEventListener(IHostMigrationEventListener* pListener)
{
#if NETWORK_HOST_MIGRATION
	for (THostMigrationEventListenerVector::iterator it = m_hostMigrationListeners.begin(); it != m_hostMigrationListeners.end(); ++it)
	{
		if (it->m_pListener == pListener)
		{
			m_hostMigrationListeners.erase(it);
			return;
		}
	}
#endif
}
/////////////////////////////////////////////////////////////////////////////

// Expose encryption method for game to use on files as necessary.

void CNetwork::EncryptBuffer(uint8* pOutput, const uint8* pInput, uint32 bufferLength, const uint8* pKey, uint32 keyLength)
{
	if (pKey && (keyLength > 0))
	{
		CStreamCipher cipher;
		cipher.Init(pKey, keyLength);

		if (pInput && pOutput && (bufferLength > 0))
		{
			cipher.Encrypt(pInput, bufferLength, pOutput);
		}
	}
}

void CNetwork::DecryptBuffer(uint8* pOutput, const uint8* pInput, uint32 bufferLength, const uint8* pKey, uint32 keyLength)
{
	if (pKey && (keyLength > 0))
	{
		CStreamCipher cipher;
		cipher.Init(pKey, keyLength);

		if (pInput && pOutput && (bufferLength > 0))
		{
			cipher.Decrypt(pInput, bufferLength, pOutput);
		}
	}
}

TCipher CNetwork::BeginCipher(const uint8* pKey, uint32 keyLength)
{
	CStreamCipher* cipher = new CStreamCipher;
	if(cipher)
		cipher->Init(pKey, keyLength);
	return cipher;
}

void CNetwork::Encrypt(TCipher cipher, uint8* pOutput, const uint8* pInput, uint32 bufferLength)
{
	if (cipher && pInput && pOutput && (bufferLength > 0))
	{
		static_cast<CStreamCipher*>(cipher)->EncryptStream(pInput, bufferLength, pOutput);
	}
}

void CNetwork::Decrypt(TCipher cipher, uint8* pOutput, const uint8* pInput, uint32 bufferLength)
{
	if (cipher && pInput && pOutput && (bufferLength > 0))
	{
		static_cast<CStreamCipher*>(cipher)->DecryptStream(pInput, bufferLength, pOutput);
	}
}

void CNetwork::EndCipher(TCipher cipher)
{
	delete static_cast<CStreamCipher*>(cipher);
}


#include UNIQUE_VIRTUAL_WRAPPER(INetwork)
